home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 40
/
Aminet 40 (2000)(Schatztruhe)[!][Dec 2000].iso
/
Aminet
/
dev
/
src
/
xad_ZAP.lha
/
ZAP.c
< prev
Wrap
C/C++ Source or Header
|
2000-08-06
|
8KB
|
274 lines
/* This XAD client is (C) 2000 Stuart Caie <kyzer@4u.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* ZAP is a lesser-known Amiga disk archiver. This client is only tested
* with ZAP v1.41. It uses byte-based run-length encoding and LZ78
* compression, which is redundant because the LZ compressor handles
* repeated byte runs really well. The archiver only supports normal
* Amiga DOS disks (80*2*11*512 = 901120 bytes), and it compresses them
* in 10 blocks of 8 cylinders. It also supports using the AmigaDOS
* disk bitmap, but just by clearing those sectors before compression,
* not some special format. Finally, it supports attaching a textfile/
* banner to the archive, shown on extraction. ZAP is (C) 1990 GREMLIN of
* MAYHEM.
*
* $VER: ZAP.c 1.2 (05.08.2000)";
*/
#include <proto/xadmaster.h>
#include <libraries/xadmaster.h>
#include <exec/memory.h>
#include <string.h>
#include "ConvertE.c"
#include "SDI_compiler.h"
#ifndef XADMASTERFILE
#define ZAP_Client FirstClient
#define NEXTCLIENT 0
UBYTE version[] = "$VER: ZAP 1.1 (05.08.2000)";
#endif
#define ZAP_VERSION 1
#define ZAP_REVISION 1
#define XADBASE REG(a6, struct xadMasterBase *xadMasterBase)
/* work-doing macros */
#define SKIP(offset) if ((err = xadHookAccess(XADAC_INPUTSEEK, \
(ULONG)(offset), NULL, ai))) goto exit_handler
#define SEEK(offset) SKIP((offset) - ai->xai_InPos)
#define READ(buffer,length) if ((err = xadHookAccess(XADAC_READ, \
(ULONG)(length), (APTR)(buffer), ai))) goto exit_handler
#define WRITE(buffer,length) if ((err = xadHookAccess(XADAC_WRITE, \
(ULONG)(length), (APTR)(buffer), ai))) goto exit_handler
#define ERROR(error) do { err = XADERR_##error; goto exit_handler; } while(0)
#define ALLOC(t,v,l) \
if (!((v) = (t) xadAllocVec((l),MEMF_CLEAR))) ERROR(NOMEMORY)
#define ALLOCOBJ(t,v,kind,tags) \
if (!((v) = (t) xadAllocObjectA((kind),(tags)))) ERROR(NOMEMORY)
#define FREE(obj) xadFreeObjectA((APTR)(obj),NULL)
#define READLONG(var) do { READ(&buffer,4); (var)=EndGetM32(buffer);} while(0)
/* main ZAP LZ78 decruncher */
struct ZAPdecr {
/* bitbuffer, checksum, input pointer, input beginning, bits left, error */
ULONG bb, sum; UBYTE *i, *in, bl, err;
};
ULONG ZAP_getlong(struct ZAPdecr *zd) {
UBYTE *in; if ((in=(zd->i -= 4)) < zd->in) {zd->err=XADERR_INPUT; return 0;}
return EndGetM32(in);
}
ULONG ZAP_getbits(struct ZAPdecr *zd, int n) {
int x=0; ULONG bb = zd->bb;
while (n--) {
if (!zd->bl--) { zd->sum ^= (bb = ZAP_getlong(zd)); zd->bl = 31; }
x = (x<<1) | (bb&1); bb >>= 1;
}
zd->bb = bb;
return x;
}
#define BITS(n) (ZAP_getbits(&zd, (n)))
#define BIT (ZAP_getbits(&zd, 1))
#define PUT(x) if(--o < out) return XADERR_OUTPUT; else *o=(UBYTE)(x)
LONG ZAP_decrunch(UBYTE *in, UBYTE *out, ULONG inlen) {
struct ZAPdecr zd;
UBYTE *match, *o;
int x, y;
/* initialise state */
zd.bb = 0;
zd.bl = 0;
zd.err = XADERR_OK;
zd.in = in;
zd.i = in + inlen;
o = out + ZAP_getlong(&zd);
zd.sum = ZAP_getlong(&zd);
/* throw away bits up to first set bit */
while (!BIT) if (zd.err) break;
while (o > out) {
if ((x=BITS(2)) == 3) x = BIT ? (BIT ? -1 : (BITS(8)+19)) : (BITS(4)+3);
if (x >= 0) do { PUT(BITS(8)); } while (x-- > 0);
if (o <= out) break;
if ((x = BITS(2)) == 3) {
x = BIT ? (BIT ? -1 : (BITS(8)+20)) : (BITS(4)+4);
if (x >= 0) y = BIT ? (BITS(13)+258) : (BITS(8)+2);
}
else {
if (x) { x++; y = BIT ? (BITS(13)+258) : (BITS(8)+2); }
else { x=1; y = BITS(8)+2; }
}
match = o + y; if (x >= 0) do { PUT(*--match); } while (x-- > 0);
}
/* checksum should be 0 on exit */
if (!zd.err && zd.sum) zd.err = XADERR_CHECKSUM;
return (LONG) zd.err;
}
/* RLE decoder for compressed blocks */
void ZAP_rledecode(UBYTE *in, UBYTE *out) {
UBYTE *i = in+9, *o = out, *end=out + EndGetM32(in+4);
int code=in[8], x, y;
while (o < end) {
if ((x = *i++) == code) {
if ((x = *i++)) { y=*i++; x+=3; while (x--) *o++=y; } else *o++ = code;
}
else *o++ = x;
}
}
ASM(BOOL) ZAP_RecogData(REG(d0, ULONG size), REG(a0, UBYTE *data), XADBASE) {
return (BOOL) (strncmp("ZAP V1.41", data+4, 9) == 0);
}
ASM(LONG) ZAP_GetInfo(REG(a0, struct xadArchiveInfo *ai), XADBASE) {
UBYTE buffer[4], *textin = NULL;
struct xadDiskInfo *xdi = NULL;
struct xadTextInfo *ti;
LONG err = XADERR_OK;
ULONG tfsize;
ALLOCOBJ(struct xadDiskInfo *, xdi, XADOBJ_DISKINFO, NULL);
ai->xai_DiskInfo = xdi;
xdi->xdi_EntryNumber = 1;
xdi->xdi_SectorSize = 512;
xdi->xdi_TotalSectors = 80 * 22;
xdi->xdi_Cylinders = 80;
xdi->xdi_CylSectors = 22;
xdi->xdi_Heads = 2;
xdi->xdi_TrackSectors = 11;
xdi->xdi_LowCyl = 0;
xdi->xdi_HighCyl = 79;
/* read and skip header ID text */
READLONG(tfsize); SKIP(tfsize);
/* read attached textfile (if it exists) */
READLONG(tfsize);
xdi->xdi_DataPos = ai->xai_InPos + tfsize;
if (tfsize) {
ALLOCOBJ(struct xadTextInfo *, ti, XADOBJ_TEXTINFO, NULL);
xdi->xdi_TextInfo = ti;
ALLOC(UBYTE *, textin, tfsize);
READ(textin, tfsize);
ti->xti_Size = EndGetM32(textin+tfsize-4);
ALLOC(STRPTR, ti->xti_Text, ti->xti_Size+1);
if ((err = ZAP_decrunch(textin, ti->xti_Text, tfsize))) goto exit_handler;
ti->xti_Text[ti->xti_Size] = '\0';
}
exit_handler:
if (textin) FREE(textin);
if (err) {
if (!xdi) return err;
ai->xai_Flags |= XADAIF_FILECORRUPT;
ai->xai_LastError = err;
}
return XADERR_OK;
}
#define ZAP_CYLSIZE (22*512) /* size of one cylinder */
#define ZAP_BLOCKSIZE (22*512*8) /* size of one block */
struct ZAPstate {
UBYTE data[ZAP_BLOCKSIZE+260], temp[ZAP_BLOCKSIZE], block;
};
ASM(LONG) ZAP_UnArchive(REG(a0, struct xadArchiveInfo *ai), XADBASE) {
struct ZAPstate *zs;
UBYTE buffer[4], cyl, block;
LONG err = XADERR_OK;
ULONG crlen;
if (!(zs = (struct ZAPstate *) ai->xai_PrivateClient)) {
ALLOC(struct ZAPstate *, zs, sizeof(struct ZAPstate));
ai->xai_PrivateClient = (APTR) zs;
zs->block = 99; /* sentinel value */
}
for (cyl = ai->xai_LowCyl; cyl <= ai->xai_HighCyl; cyl++) {
/* if the current block isn't the one needed... */
if ((block = cyl >> 3) != zs->block) {
/* if it's not just the next block we need */
if (zs->block != (block-1)) {
/* go back to start of disk and skip forward to appropriate block */
int skip = block;
SEEK(ai->xai_CurDisk->xdi_DataPos);
while (skip--) { READLONG(crlen); SKIP(crlen); }
}
/* read (possibly crunched) block into block buffer */
READLONG(crlen);
if (crlen > ZAP_BLOCKSIZE) ERROR(INPUT);
READ(zs->data, crlen);
/* if the block is crunched */
if (crlen != ZAP_BLOCKSIZE) {
/* decrunch it to temp buffer */
if (EndGetM32(zs->data+crlen-4) > ZAP_BLOCKSIZE) ERROR(OUTPUT);
if ((err = ZAP_decrunch(zs->data, zs->temp, crlen))) goto exit_handler;
/* un-RLE back to block buffer */
ZAP_rledecode(zs->temp, zs->data);
}
zs->block = block;
}
WRITE(zs->data + ((cyl & 7) * ZAP_CYLSIZE), ZAP_CYLSIZE);
}
exit_handler:
return err;
}
ASM(void) ZAP_Free(REG(a0, struct xadArchiveInfo *ai), XADBASE) {
if (ai->xai_PrivateClient) {
FREE(ai->xai_PrivateClient);
ai->xai_PrivateClient = NULL;
}
}
const struct xadClient ZAP_Client = {
NEXTCLIENT, XADCLIENT_VERSION, 4, ZAP_VERSION, ZAP_REVISION,
40, XADCF_DISKARCHIVER|XADCF_FREEDISKINFO|XADCF_FREETEXTINFO,
0, "ZAP",
(BOOL (*)()) ZAP_RecogData,
(LONG (*)()) ZAP_GetInfo,
(LONG (*)()) ZAP_UnArchive,
(void (*)()) ZAP_Free
};